﻿#include "rpc_helper.h"

#include "asio/asio_server.h"  // for GetClientMailbox()
#include "client_mailbox.h"  // for CClientMailBox
#include "pb/svr/forward_from_base.pb.h"  // for ForwardResponse
#include "pb/svr/forward_to_clt.pb.h"  // for ForwardToCltRequest
#include "get_world.h"  // for GetWorld()
#include "log.h"
#include "rpc_call_context.h"  // for GetRpcCltId()
#include "server_mailbox.h"  // for CServerMailBox
#include "world.h"  // for GetServer()

#include <google/protobuf/message.h>  // for Message

const char LOG_NAME[] = "RcpHelper";

namespace RpcHelper
{

bool ParseMsgFromStr(const std::string& sData,
	google::protobuf::Message& rMsg)
{
	if (rMsg.ParseFromString(sData))
		return true;
	LOG_WARN(Fmt("Failed to parse message. len=%u") % sData.size());
	return false;
}

void ReplyTo(const CRpcCallContext& ctx,
	const google::protobuf::Message& resp)
{
	ReplyTo(ctx, resp.SerializeAsString());
}

void ReplyTo(const CRpcCallContext& ctx, const std::string& sResp)
{
	uint64_t uRpcCltId = ctx.GetRpcCltId();
	// uRpcCltId == MailboxId
	CClientMailBox* pmb = GetWorld()->GetServer()->GetClientMailbox(uRpcCltId);
	if (!pmb)
	{
		LOG_WARN("Can not find mail box. Fd=" << uRpcCltId);
		return;
	}

	if (!ctx.IsForwarded())
	{
		pmb->PushRpcResp(ctx.GetRpcId(), sResp);
		return;
	}

	svr::ForwardResponse fwdResp;
	fwdResp.set_content(sResp);
	pmb->PushRpcResp(ctx.GetRpcId(), fwdResp);
}

void Request(uint64_t uRpcCltId, const std::string& sService,
	const std::string& sMethod, const google::protobuf::Message& req,
	const RpcCallback& cb)
{
	// uRpcCltId == MailboxId
	CClientMailBox* pmb = GetWorld()->GetServer()->GetClientMailbox(uRpcCltId);
	if (pmb)
		pmb->PushRpcReq(sService, sMethod, req, cb);
	else
		LOG_WARN("Can not find mail box to request. Id=" << uRpcCltId);
}

void Request(uint64_t uRpcCltId, const std::string& sService,
	const std::string& sMethod, const std::string& sRequest,
	const RpcCallback& cb)
{
	// uRpcCltId == MailboxId
	CClientMailBox* pmb = GetWorld()->GetServer()->GetClientMailbox(uRpcCltId);
	if (pmb)
		pmb->PushRpcReq(sService, sMethod, sRequest, cb);
	else
		LOG_WARN("Can not find mail box to request. Id=" << uRpcCltId);
}

void Request(const CGameCltId& gameCltId, const std::string& sService,
	const std::string& sMethod, const google::protobuf::Message& req,
	const RpcCallback& cb/* = RpcCallback() */)
{
	Request(gameCltId, sService, sMethod, req.SerializeAsString(), cb);
}

void Request(const CGameCltId& gameCltId, const std::string& sService,
	const std::string& sMethod, const std::string& sRequest,
	const RpcCallback& cb/* = RpcCallback() */)
{
	if (gameCltId.IsLocal())
	{
		Request(gameCltId.GetBaseRpcCltId(), sService, sMethod, sRequest, cb);
		return;
	}
	svr::ForwardToCltRequest req;
	req.set_base_rpc_clt_id(gameCltId.GetBaseRpcCltId());
	req.set_service(sService);
	req.set_method(sMethod);
	req.set_content(sRequest);
	RequestSvr(gameCltId.GetBaseSvrId(), "svr.ForwardToClt", "ForwardToClt", req,
		[cb](const std::string sContent) {
			svr::ForwardToCltResponse resp;
			if (!ParseMsgFromStr(sContent, resp)) return;
			if (cb) cb(resp.content());
		});
}

// Request between servers.
void RequestSvr(uint16_t uServerId, const std::string& sService,
	const std::string& sMethod, const google::protobuf::Message& req,
	const RpcCallback& cb)
{
	// Todo: use Mailbox instead of MailBox...
	CServerMailBox* pSvrMb = GetWorld()->GetServerMailbox(uServerId);
	if (pSvrMb)
		pSvrMb->PushRpcReq(sService, sMethod, req, cb);
	else
		LOG_WARN("Can not find server mail box to request. SvrId=" << uServerId);
}

void RequestSvr(uint16_t uServerId, const std::string& sService,
	const std::string& sMethod, const std::string& sRequest,
	const RpcCallback& cb)
{
	CServerMailBox* pSvrMb = GetWorld()->GetServerMailbox(uServerId);
	if (pSvrMb)
		pSvrMb->PushRpcReq(sService, sMethod, sRequest, cb);
	else
		LOG_WARN("Can not find server mail box to request. SvrId=" << uServerId);
}

}  // namespace RpcHelper
